
local DIY = require "packages/diy_utility/_base"

--旨在为diy提供便利，避免每个服务器造一遍轮子的utility。如modder有需要欢迎向本仓库发起PR

--原则上应该兼容原有的功能。e.g.假如你的服务器需要的是“回合结束后再将牌置入弃牌堆的中央区”，而非本utility提供的“牌一直存在于弃牌堆的中央区”

---------------------------------------------------------------------------------------------------
--中央区（本回合进入弃牌堆，且当前存在于弃牌堆的牌）

--贴有此标记的牌视为不在中央区，用于“移去中央区某些牌”。可带"-phase"、"-turn"等suffix
MarkEnum.RemoveCenterCards = "RemoveCenterCards"

--贴有此标记的弃牌堆中的牌视为在中央区，不会随回合结束清理，用于“将某些牌固定于中央区”。可带"-phase"、"-turn"等suffix
MarkEnum.IncludeCenterCards = "IncludeCenterCards"

--中央区显示相关
local CenterArea = fk.CreateTriggerSkill{
  name = "#CenterArea",
  global = true,
  priority = 0.001,

  refresh_events = {fk.AfterCardsMove, fk.AfterDrawPileShuffle, fk.AfterTurnEnd,
    fk.GamePrepared, fk.EventAcquireSkill, fk.EventLoseSkill},
  can_refresh = function(self, event, target, player, data)
    if player.seat == 1 then
      --用左上角的banner显示
      if player.room:getBanner("@$CenterArea") ~= nil then
        if event == fk.AfterCardsMove or event == fk.AfterDrawPileShuffle or event == fk.AfterTurnEnd then
          return true
        elseif event == fk.EventLoseSkill then
          return data.CenterArea and table.every(player.room.alive_players, function (p)
            return not table.find(p.player_skills, function (s)
              --仅当有中央区相关的技能在场上时，才显示中央区。使用方法为给skill加上CenterArea属性
              return s.CenterArea
            end)
          end)
        end
      else
        if event == fk.GamePrepared then
          return table.find(player.room.alive_players, function (p)
            return table.find(p.player_skills, function (s)
              return s.CenterArea
            end)
          end)
        elseif event == fk.EventAcquireSkill then
          return data.CenterArea
        end
      end
    end
  end,
  on_refresh = function(self, event, target, player, data)
    local room = player.room
    if event == fk.EventLoseSkill then
      room:setBanner("@$CenterArea", nil)
    elseif event == fk.AfterTurnEnd then
      local ids = room:getBanner("@$CenterArea")
      if #ids > 0 then
        room.logic:trigger("fk.BeforeCenterAreaRemove", nil, {ids = ids})
      end
      room:setBanner("@$CenterArea", {})
      if #ids > 0 then
        room.logic:trigger("fk.AfterCenterAreaRemove", nil, {ids = ids})
      end
    else
      local cards = table.filter(room.discard_pile, function (id)
        return Fk:getCardById(id):getMark(MarkEnum.IncludeCenterCards) > 0 or
          table.find(MarkEnum.TempMarkSuffix, function (suffix)
            return Fk:getCardById(id):getMark(MarkEnum.IncludeCenterCards..suffix) > 0
          end)
      end)
      room.logic:getEventsOfScope(GameEvent.MoveCards, 1, function(e)
        for _, move in ipairs(e.data) do
          if move.toArea == Card.DiscardPile then
            for _, info in ipairs(move.moveInfo) do
              table.insertIfNeed(cards, info.cardId)
            end
          end
        end
      end, Player.HistoryTurn)
      cards = table.filter(cards, function (id)
        local card = Fk:getCardById(id)
        return table.contains(room.discard_pile, id) and
          table.every(MarkEnum.TempMarkSuffix, function (suffix)
            return card:getMark(MarkEnum.RemoveCenterCards..suffix) == 0
          end)
      end)
      local orig_ids = room:getBanner("@$CenterArea") or {}
      local ids = table.filter(orig_ids, function (id)
        return not table.contains(cards, id)
      end)
      if #ids > 0 and event ~= fk.AfterCardsMove then
        room.logic:trigger("fk.BeforeCenterAreaRemove", nil, {ids = ids})
      end
      room:setBanner("@$CenterArea", cards)
      if #ids > 0 and event ~= fk.AfterCardsMove then
        room.logic:trigger("fk.AfterCenterAreaRemove", nil, {ids = ids})
      end
    end
  end,
}
Fk:addSkill(CenterArea)
Fk:loadTranslationTable{
  ["@$CenterArea"] = "中央区",
  ["$CenterArea"] = "中央区",
}

--将一些牌移出中央区（仍在弃牌堆内，但不计入中央区计算）
---@param player ServerPlayer @ 操作的角色
---@param card integer | integer[] | Card | Card[] @ 要移出的牌
---@param scope? integer @ 移出的历史范围，默认当回合
DIY.RemoveCenterCards = function (player, card, scope)
  local room = player.room
  local banner = room:getBanner("@$CenterArea") or {}
  if #banner == 0 then return end
  local suffix_mapper = {
    [Player.HistoryGame] = "",
    [Player.HistoryRound] = "-round",
    [Player.HistoryTurn] = "-turn",
    [Player.HistoryPhase] = "-phase",
  }
  scope = scope or Player.HistoryTurn
  local ids = Card:getIdList(card)
  ids = table.filter(ids, function (id)
    return table.contains(banner, id)
  end)
  if #ids > 0 then
    for _, id in ipairs(ids) do
      table.removeOne(banner, id)
      room:setCardMark(Fk:getCardById(id), MarkEnum.RemoveCenterCards..suffix_mapper[scope], 1)
    end
    room.logic:trigger("fk.BeforeCenterAreaRemove", player, {ids = ids})
    room:setBanner("@$CenterArea", banner)
    room.logic:trigger("fk.AfterCenterAreaRemove", player, {ids = ids})
  end
end

---------------------------------------------------------------------------------------------------
--明置牌（将一些手牌对所有角色可见。官方仅国际服国战田豫有此功能）

--贴有此标记的牌在手牌中明置，离开手牌时清除。可带"-phase"、"-turn"等suffix
MarkEnum.ShownCards = "@@ShownCards-inhand"

--明置手牌
---@param player ServerPlayer @ 目标角色
---@param card integer | integer[] | Card | Card[] @ 要明置的牌
---@param scope? integer @ 明置的历史范围，默认永久
DIY.ShowCards = function (player, card, scope)
  local suffix_mapper = {
    [Player.HistoryGame] = "",
    [Player.HistoryRound] = "-round",
    [Player.HistoryTurn] = "-turn",
    [Player.HistoryPhase] = "-phase",
  }
  scope = scope or Player.HistoryGame
  local ids = Card:getIdList(card)
  ids = table.filter(ids, function (id)
    return table.contains(player:getCardIds("h"), id)
  end)
  if #ids > 0 then
    for _, id in ipairs(ids) do
      player.room:setCardMark(Fk:getCardById(id), MarkEnum.ShownCards..suffix_mapper[scope], 1)
    end
    player:showCards(ids)
  end
end

--暗置手牌
---@param player ServerPlayer @ 目标角色
---@param card integer | integer[] | Card | Card[] @ 要暗置的牌
DIY.HideCards = function (player, card)
  local room = player.room
  local ids = Card:getIdList(card)
  ids = table.filter(ids, function (id)
    return table.contains(player:getCardIds("h"), id)
  end)
  if #ids > 0 then
    for _, id in ipairs(ids) do
      room:setCardMark(Fk:getCardById(id), MarkEnum.ShownCards, 0)
      for _, suffix in ipairs(MarkEnum.TempMarkSuffix) do
        room:setCardMark(Fk:getCardById(id), MarkEnum.ShownCards..suffix, 0)
      end
    end
  end
end

--获得一名角色的明置手牌id列表
---@param player ServerPlayer | Player @ 目标角色
---@return integer[]
DIY.GetShownCards = function (player)
  return table.filter(player:getCardIds("h"), function (id)
    return Fk:getCardById(id):getMark(MarkEnum.ShownCards) > 0 or
      table.find(MarkEnum.TempMarkSuffix, function (suffix)
        return Fk:getCardById(id):getMark(MarkEnum.ShownCards..suffix) > 0
      end)
  end)
end

local ShownCards = fk.CreateVisibilitySkill{
  name = "#ShownCards",
  global = true,
  card_visible = function(self, player, card)
    if card:getMark(MarkEnum.ShownCards) > 0 or
      table.find(MarkEnum.TempMarkSuffix, function (suffix)
        return card:getMark(MarkEnum.ShownCards..suffix) > 0
      end) then
      return true
    end
  end
}
Fk:addSkill(ShownCards)
Fk:loadTranslationTable{
  ["@@ShownCards-inhand"] = "明置",
  ["@@ShownCards-inhand-round"] = "明置",
  ["@@ShownCards-inhand-turn"] = "明置",
  ["@@ShownCards-inhand-phase"] = "明置",
}

---------------------------------------------------------------------------------------------------

--- 进入隐匿状态（身份局的开局隐匿在游戏流程中，此函数适用于diy的主动进入隐匿状态）
---@param player ServerPlayer @ 目标角色
DIY.EnterHidden = function (player)
  local room = player.room
  room:sendLog({
    type = "#EnterHidden",
    from = player.id,
  })
  local skills = "hidden_skill&"
  room:setPlayerMark(player, "__hidden_general", player.general)
  for _, s in ipairs(Fk.generals[player.general]:getSkillNameList(true)) do
    if player:hasSkill(s, true) then
      skills = skills.."|-"..s
    end
  end
  if player.deputyGeneral ~= "" then
    room:setPlayerMark(player, "__hidden_deputy", player.deputyGeneral)
    for _, s in ipairs(Fk.generals[player.deputyGeneral]:getSkillNameList(true)) do
      if player:hasSkill(s, true) then
        skills = skills.."|-"..s
      end
    end
  end
  player.general = "hiddenone"
  player.gender = General.Male
  room:broadcastProperty(player, "gender")
  if player.deputyGeneral ~= "" then
    player.deputyGeneral = ""
  end
  player.kingdom = "jin"
  room:setPlayerMark(player, "__hidden_record",
  {
    maxHp = player.maxHp,
    hp = player.hp,
  })
  player.maxHp = 1
  player.hp = 1
  for _, property in ipairs({"general", "deputyGeneral", "kingdom", "maxHp", "hp"}) do
    room:broadcastProperty(player, property)
  end
  room:handleAddLoseSkills(player, skills, nil, false, true)
end
Fk:loadTranslationTable{
  ["#EnterHidden"] = "%from 进入隐匿状态",
  ["enter_hidden_href"] = "记录当前的体力上限和体力值，失去当前武将牌上的所有技能，将武将替换为隐匿将。再次登场后，"..
  "将体力上限和体力值调整为记录值，武将替换回隐匿前的武将，获得武将牌上所有的技能。",
}

---------------------------------------------------------------------------------------------------

---@param player Player
local checkRemove = function (player)
  local markName = "@@remove_player"
  for mark, _ in pairs(player.mark) do
    if mark == markName or mark == markName .. "_list" then return true end
    if mark:startsWith(markName .. "-") then
      for _, suffix in ipairs(MarkEnum.TempMarkSuffix) do
        if mark:find(suffix, 1, true) then return true end
      end
    end
  end
end

local remove_player_prohibit = fk.CreateProhibitSkill{
  name = "#remove_player_prohibit",
  prohibit_use = function(self, player, card)
    return card and checkRemove(player)
  end,
  is_prohibited = function(self, from, player, card)
    return card and checkRemove(player)
  end,
}
local remove_player_trigger = fk.CreateTriggerSkill{
  name = "#remove_player_trigger",

  refresh_events = {fk.PreHpRecover, fk.PreHpLost, fk.DamageInflicted},
  can_refresh = function(self, event, target, player, data)
    return target == player and checkRemove(player)
  end,
  on_refresh = function(self, event, target, player, data)
    if event == fk.DamageInflicted then
      data.damage = 0
    else
      data.num = 0
    end
  end,
}
Fk:addSkill(remove_player_trigger)
Fk:addSkill(remove_player_prohibit)

--- 将角色移出游戏
--- 复用调虎离山概念。指角色于不计入距离和座次的计算，不能使用牌且不是牌的合法目标，且防止体力值改变。
---@param player ServerPlayer @ 目标角色
---@param temp? string @ 作用时间范围，``-round`` ``-turn`` ``-phase`` 若填其他内容则需调用returnPlayer函数，输入相同的temp参数返回游戏
DIY.removePlayer = function (player, temp)
  local room = player.room
  room.logic:addTriggerSkill(remove_player_trigger)
  room:addSkill("#remove_player_prohibit")
  if temp == nil then temp = "" end
  room:addPlayerMark(player, MarkEnum.PlayerRemoved..temp, 1)
  if table.contains(MarkEnum.TempMarkSuffix, temp) or temp == "" then
    room:setPlayerMark(player, "@@remove_player"..temp, 1)
  else
    room:addTableMark(player, "@@remove_player_list", temp)
  end
end


--- 将角色返回游戏（解除移出游戏状态）
---@param player ServerPlayer @ 目标角色
---@param temp string @ 移出游戏标识
DIY.returnPlayer = function (player, temp)
  player.room:removeTableMark(player, "@@remove_player_list", temp)
end


Fk:loadTranslationTable{
  ["#remove_player_prohibit"] = "移出游戏",
  ["@@remove_player"] = "移出游戏",
  ["@@remove_player-turn"] = "移出游戏",
  ["@@remove_player-phase"] = "移出游戏",
  ["@@remove_player-round"] = "移出游戏",
  ["@@remove_player_list"] = "移出游戏",
}




return DIY
